/*
 * Copyright 2008 Lucas Tan.
 * This copyright notice must always remain intact whenever you use or distribute 
 * the source code contained in this file.
 * Any usage of any code in this file, either in derived work or
 * through the use of external linkage,
 * must always be credited with the author's name.
 * 
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 * DEALINGS IN THE SOFTWARE.
 * 
 * */
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;

namespace PassKeeper.Data
{
	public abstract class Element : ICloneable
	{
		private readonly Database _owner;

		private Element _parent;

		protected Element()
		{
			/*Owner can be null if the sub class is Database.*/
			_owner = null;
		}

		protected Element(Database owner)
		{
			if (owner == null)
			{
				throw new ArgumentNullException("owner");
			}

			_owner = owner;
		}

		/// <summary>
		/// Tests whether a specified <see cref="Element"/> is a direct or indirect parent
		/// of this object.
		/// </summary>
		/// <param name="elem">The specified object.</param>
		/// <returns>true if <paramref name="elem"/> is a direct/indirect parent of this object;
		/// false otherwise.</returns>
		/// <exception cref="ArgumentNullException"><paramref name="elem"/> is null.</exception>
		public bool IsAncestor(Element elem)
		{
			if (elem == null)
			{
				throw new ArgumentNullException("elem");
			}

			Element curParent = this.Parent;

			while (curParent != null)
			{
				if (object.ReferenceEquals(curParent, elem))
				{
					return true;
				}

				curParent = curParent.Parent;
			}

			return false;
		}

		object ICloneable.Clone()
		{
			return Clone(this.OwnerDatabase);
		}

		public Element Clone()
		{
			return Clone(this.OwnerDatabase);
		}

		public abstract void Deserialize(XmlElement elem);

		public abstract void Serialize(XmlElement elem);

		protected internal abstract Element Clone(Database ownerDatabase);

		public abstract string TagName
		{
			get;
		}

		public Database OwnerDatabase
		{
			get
			{
				return _owner;
			}
		}

		public virtual Element Parent
		{
			get
			{
				return _parent;
			}
			protected internal set
			{
				if (value == null)
				{
					_parent = null;
				}
				else
				{
					if (object.ReferenceEquals(this, value))
					{
						throw new ArgumentException("Cannot be a parent of itself.", "value");
					}

					if (value.IsAncestor(this))
					{
						throw new ArgumentException("Cannot be a child of its child.", "value");
					}

					_parent = value;
				}
			}
		}
	}
}
